! Future Vision v4.0d12
! *[ FutureNet Datagram Protocol (FDP) ]*
!
! Written by Joshua M. Thompson
! Copyright (c) 1990-92 Frontier Technologies, Inc.

! Format of ROUTERS file
!
! Router information record:
!
!  +0 : Site ID on source network 					2 bytes
!  +2 : Site ID on destination network					2 bytes
!  +4 : Destination network						2 bytes
!  +6 : Router flags, bitmapped 					4 bytes
!
!	    bit 0 : Router is a gateway (0=No, 1=Yes)
!	bits 1-31 : Reserved, set to zeroes.
!
! +10 : Reserved, set to zeroes for future compatibility		6 bytes
!
! Destination Descriptor Format:
!
!   SITE(0) = Number of descriptors
!   FLAG(x) = Descriptor flags for descriptor #x
! SITE(x,0) = Number of nodes in descriptor #x
! SITE(x,y) = Site IDs of nodes in descriptor #x
!  NET(x,y) = Net IDs of nodes in descriptor #x

! Error codes:
!
! $0301 = FDP Not Initialized
! $0302 = FDP Already Initialized
! $0303 = Client ID already in use
! $0304 = Invalid client ID/owner error
! $0305 = Invalid route

DispatchFDP
 if func>9 gosub IntFDP_CheckStarted
 on func goto FDP_BootInit,FDP_Startup,FDP_Shutdown,FDP_Status,FDP_Version, ->
              ]err,]err,]err,FDP_SystemTask, ->
              FDP_ReadDatagram,FDP_WriteDatagram, ->
              FDP_CheckBuffer,FDP_FreeBuffer, ->
              FDP_AddID,FDP_DeleteID
]err
 err=$0001:return

FDP_BootInit
 fdp$(0)="4/FDP/":create fdp$(0),15
 fdp$(1)=fdp$(0)+"INCOMING"		! Incoming datagrams spool file
 fdp$(2)=fdp$(0)+"CLIENTS"		! FDP Client ID list
 fdp$(3)=fdp$(0)+"ROUTERS"		! Internet routers list
 fdp$(4)=fdp$(0)+"ROUTES."		! Internet shortest routes maps
 fdp$(9)=fdp$(0)+"FDP.TEMP"		! Temporary file
 create fdp$(1),6
 return

FDP_Startup
 if fdp(0) err=$0302:return
 gosub LM_GetNumProts
 fdp(0,0)=nump:if not nump goto ]exit
 prot=0
]L
 prot==+1:if prot>nump goto ]exit
 gosub LM_GetProtocolInfo
 fdp(prot,0)=net:fdp(prot,1)=site
 goto ]L
]exit
 fdp(0)=1:return

FDP_Shutdown
 gosub IntFDP_CheckStarted
 fdp(0)=0:return

FDP_Status
 x=fdp(0):return

FDP_Version
 vers=$8100:return

! Perform requried FDP maintenance tasks.  All installed LAPs are checked for
! incoming datagrams, and any found are routed appropriately.  FDP command
! packets are also handled here.
!
! Entry conditions:
!
! (none)
!
! Exit conditions:
!
! (none)
!
! Error codes:
!
! FDP Not Initialized

! System Tasks include:
!
! (1). Pole all installed protocols for FDP data packets and route them
! (2). Pole all installed protocols for FDP control packets and execute
!      them.

FDP_SystemTask
 gosub IntFDP_CheckStarted
 gosub LM_GetNumProts
 gosub SaveFile1
 prot=0
 ? "Routing datagrams: [000]";
]dataLoop
 prot==+1:if prot>nump goto ]dataDone
 ? ""str$(prot,3)"][00000]";
 flags=%1100:clid=$0100:bpos=0:buff$=""
 dtyp(0)=0:dtyp(1)=fdp(prot,1):gosub LM_CheckBuffer
 if err gosub NET_LogError
]dataLoop0
 if nump=0 ? "";:goto ]dataLoop
 ? str$(nump,5)"]";:nump==-1
 flags=%1111:clid=$0100:bpos=0:buff$=fdp$(9)
 dtyp(0)=0:dtyp(1)=ourid
 gosub LM_ReadPacket:if err gosub NET_LogError:goto ]dataLoop0
 open #1,fdp$(9):bload #1,ntmp,20
 site(0)=peekword(ntmp+18)
 for &1=1 to site(0)
    bload #1,ntmp,4:flag(&1)=peekword(ntmp):site(&1,0)=peekword(ntmp+2)
    bload #1,ntmp,site(&1,0)*4
    for &2=1 to site(&1,0)
       site(&1,&2)=peekword(ntmp-4+(&2*4))
       net(&1,&2)=peekword(ntmp-2+(&2*4))
    next
 next
 close #1
 &2=prot
]dataLoop1
 gosub IntFDP_Reconcile(fdp(&2,0),fdp(&2,1))(&1)
 if &1=-1 goto ]local
 if &1=-2 goto ]dataLoop1
 if &1=0 prot=&2:goto ]dataLoop0
 src=0:flags=0:clid=$0100:prot=&1
 buff$=fdp$(9):dtyp(0)=1:dtyp(1)=0:gosub LM_WritePacket
 if err gosub NET_LogError
 goto ]dataLoop1
]local
 mclear ntmp to 0 for 256
 pokeword ntmp,2	! Command 2 = Write Local
 memstring$(ntmp+128)=fdp$(9)
 ext #$53,ntmp,site(0),site(0),net(0)
 err=peekword(ntmp+4)
 goto ]dataLoop1
]dataDone
 ? "       Done!]"cr
 return

! Read an FDP datagram from the network
!
! Entry conditions:
!
! BUFF$ = buffer file to hold returned datagram, if any
!  CLID = Client ID to match datagrams against.  Only datagrams with this ID
!         will be returned.
! FLAGS = Datagram read flags:
!              bit 0 : Return datagram data (0 = No, 1 = Yes)
!              bit 1 : Delete after reading (0 = No, 1 = Yes)
!              bit 2 : Ignore deleted datagrams (0 = No, 1 = Yes)
!              bit 3 : Check client ID (0 = No, 1 = Yes)
!          bits 4-31 : Reserved; set to zeroes
! BPOS = Buffer index position at which to begin reading.
!
! Exit conditions:
!
!   BPOS = New buffer position
!   CLID = Client ID of datagram read
!  FLAGS = Datagram flags of datagram read
!   SRC1 = Source site ID of datagram read
!   SRC2 = Source net ID of datagram read
!   SIZE = Data size of datagram read
! FLAG() = \
! SITE() =  > Destination Descriptors
!  NET() = /
!
! Error codes:
!
! FDP Not Started
! Invalid Client ID

FDP_ReadDatagram
 gosub IntFDP_CheckIDowner
 mclear ntmp to 0 for 256
 pokeword ntmp,1	! Command 1 = Read Datagram
 pokeaddr ntmp+6,flags
 pokeword ntmp+10,clid
 pokeaddr ntmp+12,bpos
 memstring$(ntmp+128)=buff$
 ext #$53,ntmp,site(0),site(0),net(0)
 err=peekword(ntmp+4)
 clid=peekword(ntmp+16)
 src1=peekword(ntmp+18)
 src2=peekword(ntmp+20)
 flags=peekaddr(ntmp+22)
 size=peekaddr(ntmp+26)
 return

! Write an FDP datagram to the network
!
! Entry conditions:
!
!  BUFF$ = buffer file holding datagram's data
!   CLID = FDP Client ID for this datagram
!   SRC1 = Source site number for this datagram or zero for local ID
!   SRC2 = Source net number for this datagram or zero for local net
! FLAG() = \
! SITE() =  > Destination Descriptors
!  NET() = /
!
! Exit conditions:
!
! Datagram is written to the network.
!
! Error codes:
!
! (LAP Errors)
! FDP Not Initialized
! Invalid Client ID

FDP_WriteDatagram
 gosub IntFDP_CheckIDowner
 if site(0)=0 return
 gosub ]calcSize()(&1,&2)
 if not &2 return
 gosub SaveFile1
 &1$=fdp$(0)+"datagram":create &1$,6:open #1,&1$
 if src1=0 src1=ourid
 if src2=0 src2=ournet
 mclear ntmp to 0 for 20
 pokeaddr ntmp,&1
 pokeword ntmp+4,src1
 pokeword ntmp+6,src2
 pokeword ntmp+8,clid
 pokeaddr ntmp+10,flags
 pokeaddr ntmp+14,&2
 pokeword ntmp+18,site(0)
 bsave #1,ntmp,20
 if doserr goto ]doserr
 for &3=1 to site(0)
    pokeword ntmp,flag(&3):pokeword ntmp+2,site(&3,0):bsave #1,ntmp,4
    if doserr pop next:goto ]doserr
    for &4=1 to site(&3)
       pokeword ntmp,site(&3,&4):pokeword ntmp+2,net(&3,&4):bsave #1,ntmp,4
       if doserr pop next:pop next:goto ]doserr
    next
 next
 fcopy buff$ to #1 mode(0) len(&2)
 if doserr goto ]doserr
 filesize(1)=filemark(1):close #1
 gosub RestoreFile1
 &2$=buff$
 pushvar clid,flags,src,buff$
 src=ourid:clid=$0100:flags=0:buff$=&1$
]another
 gosub IntFDP_Reconcile(src2,src1)(&1)
 if &1=0 goto ]done
 if &1=-1 goto ]local
 if &1=-2 err=$0305:goto ]done
 gosub LM_WritePacket
 if not err goto ]another
]done
 pullvar buff$,src,flags,clid
 return
]doserr
 err=$FF00+doserr:close #1:gosub RestoreFile1:return
]calcSize
 &1=20+(site(0)*4)
 for &2=1 to site(0)
    &1==+(site(&2,0)*2)
 next
 &2=filesize(buff$)
 return
]local
 mclear ntmp to 0 for 256
 pokeword ntmp,2	! Command 2 = Write Local
 memstring$(ntmp+128)=buff$
 ext #$53,ntmp,site(0),site(0),net(0)
 err=peekword(ntmp+4):if err goto ]done
 goto ]another

! Count the number of datagrams of a specified type in the buffer
!
! Entry conditions:
!
! FLAGS = Search Flags.  This is a bitmapped value as follows:
!
!              bit 0 : Return datagram data (0 = No, 1 = Yes)
!              bit 1 : Delete after reading (0 = No, 1 = Yes)
!              bit 2 : Ignore deleted datagrams (0 = No, 1 = Yes)
!              bit 3 : Check client ID (0 = No, 1 = Yes)
!	   bits 4-15 : Reserved; set to zero for future compatibility.
!  CLID = Client ID.  When bit 3 is set in the flag word, only datagrams with
!	  this client ID will be counted.
!  BPOS = Buffer position.  This value specified the datagram in the buffer
!	  at which the search should begin.  Datagram numbering starts at one.
!
! Exit conditions:
!
! NUMP = Number of datagrams counted
!
! Error codes:
!
! FDP Not Initialized
! Invalid client ID

FDP_CheckBuffer
 gosub IntFDP_CheckIDowner
 mclear ntmp to 0 for 256
 pokeword ntmp,0	! Command 0 = Count Datagrams
 pokeaddr ntmp+6,flags
 pokeword ntmp+10,clid
 pokeaddr ntmp+12,bpos
 memstring$(ntmp+128)=""
 ext #$53,ntmp,site(0),site(0),net(0)
 err=peekword(ntmp+4)
 nump=peekaddr(ntmp+26)
 return

! Free the buffer file of all deleted datagrams.
!
! Entry conditions:
!
! (None)
!
! Exit conditions:
!
! (None)
!
! Error codes:
!
! FDP Not Initialized

FDP_FreeBuffer
 ext #$54,fdp$(1),fdp$(9)
 return

! Add the specified client ID to FDP's list of valid IDs.
!
! Entry conditions:
!
! CLID = Client ID to allocate
!
! Exit conditions:
!
! ID is allocated using current Network Manager application name.  Only the
! same application (or the system SuperUser program) can delete this ID later.
!
! Error codes:
!
! FDP Not Initialized
! Client ID already in use

FDP_AddID
 gosub IntFDP_ReadIDrec(clid)(&1$)
 if &1$<>"" err=$0303:return
 gosub ParmNET_GetCurrApp()(&1,&1$)
 gosub IntFDP_WriteIDrec(clid,&1$)
 return

! Remove the specified client ID from FDP's list of valid IDs.
!
! Entry conditions:
!
! CLID = Client ID to remove
!
! Exit conditions:
!
! Client ID is deallocated.
!
! Error codes:
!
! FDP Not Initialized
! Invalid client ID/owner error

FDP_DeleteID
 gosub IntFDP_CheckIDowner(clid)
 gosub IntFDP_WipeIDrec(clid)
 return

! Make sure FDP is started up.

IntFDP_CheckStarted
 if not fdp(0) err=$0301:pop
 return

! Reconcile the current SITE()/FLAG()/SITE()/NET() routing table into a DEST()
! list for the LAP Manager.

IntFDP_Reconcile
 dest(0)=0
 for &3=1 to site(0)
    if not (flag(&3) b.and 1) pop next:goto ]found
 next
 &1=0:return
]found
 gosub IntFDP_BuildDest(&1,&2,&3)(&4)
 if &4=-2 flag(&3)==+1:goto IntFDP_Reconcile
 dest(0)=0
 for &3=1 to site(0)
    long if not (flag(&3) b.and 1)
        flag(&3)==+1
        gosub IntFDP_BuildDest(&1,&2,&3)(&5)
        if &5=-2 pop if:pop next:&1=-2:return
        if &5<>&4 dest(0)==-1
     end if
 next
 &1=&4:return

IntFDP_BuildDest
 if site(&3,0)=1 goto ]autoRoute	! One site total is full autorouting
 &5=-1
 for &4=1 to site(&3,0)
    if net(&3,&4)<>net(&3,&5) &5=&4	! Find start of the subroute we're in
    if (net(&3,&4)=&1) and (site(&3,&4)=&2) pop next:goto ]fndsub
 next
]routeErr
 &1=-2:return
]ours
 &1=-1:return
]fndsub
 if &4=site(&3,0) goto ]ours	! Is it ours datagram?
 gosub IntFDP_FindRouteEnd(&3,&5)(&6)
 if &4=&5 goto ]buildDest	! We're the entry router.  Send it to the
				! other end.
 if &4<>&6 goto ]routeErr	! We must be the first or last site though.
 &4=&6+1			! Bump up to next subroute
 gosub IntFDP_FindLocalID(net(&3,&4),site(&3,&4))(&7) ! Is it still us?
 if not &7 goto ]routeErr	! Nope, routers don't match, ROUTE ERROR
 &5=&4:goto ]fndsub		! Start again with new subroute
]buildDest
 dest(0)==+1			! One dest to go, please.
 if (&6-&5)<2 dest(dest(0))=site(&3,&6):goto ]exit ! If <3 sites, autoroute it
 &8=0
 for &7=1 to (&6-&5+1)
    &9=site(&3,&5+&-1)			! Site #0 entries are placeholders to
    if &9 &8==+1:dest(dest(0),&8)=&9    ! allow 2-site custom routes, because
 next					! FDP requires at least 3 entries in a
 dest(dest(0))=0:dest(dest(0),0)=&8	! custom route.
]exit
 return
]autoRoute
! First, check to see if it's reached its final network !
 for &7=1 to fdp(0,0)
    if fdp(&7,0)=net(&3,1) pop next:goto ]autoRoute0
 next
 goto ]autoRoute0a
]autoRoute0
! Ok, it's going to our network, it is us or someone else? !
 if fdp(&7,1)=site(&3,1) goto ]ours
! Nope, it's someone else...just send it to them. !
 dest(0)==+1:dest(dest(0))=site(&3,1):&1=&7:return
]autoRoute0a
! Okay, find the next hop network number to get to the destination network !
 &1$=fdp$(4)+hexstr$(&1,4,1)
 gosub SaveFile1:open #1,&1$:position #1,16,net(&3,1)
 bload #1,ntmp,16:close #1
 &4=peekword(ntmp):if not &4 gosub RestoreFile1:goto ]routeErr
! Note that net numbers 0-65534 are stored as 1-65535, so that we can use !
! the same map generator as MLAP (it doesn't support node #0)             !
 &4==+1:open #1,fdp$(3):position #1,256,&1
 for &5=1 to 16
    bload #1,ntmp,16:if peekword(ntmp+4)=&4 pop next:goto ]autoRoute1
 next
 close #1:gosub RestoreFile1:goto ]routeErr
]autoRoute1
 close #1:gosub RestoreFile1
! If it turns out that we're the router, restart the algorithm using !
! the next hop network as the source (goes to the "next next" hop)   !
 if peekword(ntmp)=&2 &1=&4:&2=peekword(ntmp+2):goto ]autoRoute
 dest(0)==+1:dest(dest(0))=peekword(ntmp)
 gosub IntFDP_FindLocalID(&2,&1)(&1)	! Get the protocol number
 return

! Search descriptor &1 for the end of the route that begins at index &2.
! The ending index is returned in &1.

IntFDP_FindRouteEnd
 &3=&2:&4=net(&1,&2)
 while (&3<site(&1,0)) and (net(&1,&3)=&4)
    &3==+1
 end while
 if net(&1,&3)<>&4 &3==-1
 &1=&3:return

! Return owner of client ID &1 in &1$.  Returns null if channel not allocated

IntFDP_ReadIDrec
 gosub SaveFile2
 open #2,fdp$(2):position #2,64,&1:input #2,&2$ cr &1$:close #2
 gosub RestoreFile2
 if &2$<>"*-* VaLiD *-*" &1$=""
 return

! Allocate client ID &1 to program &2$

IntFDP_WriteIDrec
 gosub SaveFile2
 open #2,fdp$(2):position #2,64,&1:? #2,"*-* VaLiD *-*" cr &2$:close #2
 gosub RestoreFile2
 return

! Wipe (clear) client ID &1 to an unowned status.

IntFDP_WipeIDrec
 gosub SaveFile2
 open #2,fdp$(2):position #2,64,&1:? #2,"*-* WiPeD *-*":close #2
 gosub RestoreFile2
 return

! Check the owner of client ID &1 and make sure it's the current app

IntFDP_CheckIDowner
 gosub ParmNET_GetCurrApp()(&2,&2$)
 gosub IntFDP_ReadIDrec(&1)(&1$)
 if &1$<>&2$ err=$0304:pop
 return

! Attempt to find net &1/site &2 in the list connected networks
! Returns &1=0 if no match and &1=index if match.

IntFDP_FindLocalID
 if not fdp(0,0) &1=0:return
 for &3=1 to fdp(0,0)
    if (fdp(&3,0)=&1) and (fdp(&3,1)=&2) &1=&3:pop next:return
 next
 &1=0:return
